#include <iostream>

#include <stdlib.h>
#include <ctype.h>

#include <nemesis.Logger.h>
#include <nemesis.TraceWriter.h>

#include <nemesis.xml.Node.h>
#include <nemesis.xml.Compiler.h>

#include <nemesis.shmem.StorageArea.h>
#include <nemesis.shmem.DataBlock.h>

using namespace std;
using namespace nemesis;

struct MyData {
   int number;
   char text [64];
};

const char* readString (const char* title = 0)
{
   static char s [128];

   if (title)
      cout << title << ": " << flush;

   while (!gets (s));
   
   s [63] = 0;

   return s;
}

int readInteger (const char* title = 0)
{
   return atoi (readString (title));
}

char readChar ()
{
   return *readString ();
}

void create (shmem::StorageArea& sa) 
   throw (RuntimeException)
{
   int key;
   MyData* data;

   cout << "Creacion de entrada" << endl;
   key = readInteger ("Nueva clave");
   
   shmem::DataBlock shdata = sa.create (key);
   data = reinterpret_cast <MyData*> (shdata.getData ());
   
   data->number = readInteger ("Number");
   nemesis_strcpy (data->text, readString ("Text"));
   
   LOGINFORMATION (
      string msg = functions::asString ("create | Buffer: 0x%x | Key: %d { Number=%d | Text: %s }", (int) data, key, data->number, data->text);
      Logger::information (msg, FILE_LOCATION)
   );      
   
   sa.unlock (key);
   cout << "Clave " << key << " creada" << endl << endl;
}

void find (shmem::StorageArea& sa) 
   throw (RuntimeException)
{
   int key;
   MyData* data;

   cout << "Busqueda de entrada" << endl;

   key = readInteger ("Clave buscada");
   
   shmem::DataBlock shdata = sa.find (key);
   
   data = reinterpret_cast <MyData*> (shdata.getData ());

   LOGINFORMATION (
      string msg = functions::asString ("find | Buffer: 0x%x | Key: %d { Number=%d | Text: %s }", (int) data, key, data->number, data->text);
      Logger::information (msg, FILE_LOCATION)
   );      

   cout << "Clave: " << key << endl;
   cout << "Number: " << data->number << endl;
   cout << "Text: " << data->text << endl << endl;
}

void update (shmem::StorageArea& sa) 
   throw (RuntimeException)
{
   int key;
   MyData* data;
   
   cout << "Modificacion de entrada" << endl;

   key = readInteger ("Clave buscada");
   
   shmem::DataBlock shdata = sa.lock (key);
   
   data = reinterpret_cast <MyData*> (shdata.getData ());

   LOGINFORMATION (
      string msg = functions::asString ("update | Buffer: 0x%x | Key: %d { Number=%d | Text: %s }", (int) data, key, data->number, data->text);
      Logger::information (msg, FILE_LOCATION)
   );      

   cout << "Clave: " << key << endl;
   cout << "Number actual: " << data->number << endl;
   cout << "Text actual: " << data->text << endl << endl;
      
   data->number = readInteger ("Number Nuevo");
   nemesis_strcpy (data->text, readString ("Text nuevo"));
   
   sa.unlock (key);   
   
   cout << "Clave " << key << " modificada" << endl << endl;   
}

void release (shmem::StorageArea& sa) 
   throw (RuntimeException)
{
   int key;
   MyData* data;
   
   cout << "Borrado de entrada" << endl;
   
   key = readInteger ("Clave buscada");   
   sa.lock (key);
   sa.release (key);
   cout << "Clave " << key << " borrada" << endl << endl;
}

void asXML (shmem::StorageArea& sa) 
   throw (RuntimeException)
{
   xml::Node node ("shmem");
      
   sa.asXML (&node);      
   xml::Compiler xmlCompiler;      
   cout << xmlCompiler.apply (&node) << endl << endl;
}

void report (shmem::StorageArea& sa) 
   throw (RuntimeException)
{
   int key;
   MyData* data;

   for (shmem::StorageArea::iterator ii = sa.begin (), maxii = sa.end (); ii != maxii; ii ++) {
      key = shmem::StorageArea::key (ii);
   
      shmem::DataBlock shdata = sa.find (key);
      
      data = reinterpret_cast <MyData*> (shdata.getData ());

      cout << "Clave: " << key << " | ";
      cout << "Number: " << data->number << " | ";
      cout << "Text: " << data->text << endl;      
   }
   
   cout << endl << endl;
}

int main (int argc, const char** argv)
{
   const int key = 1000;
   const int maxData = 10;
   char opt;
   shmem::StorageArea storageArea (key, maxData, sizeof (MyData));

   try {
      Logger::setLevel (Logger::Debug);
      Logger::initialize ("shmem", new TraceWriter ("file.trace", 1048000));

      storageArea.initialize ();      

      do {
         cout << "1.- Crear entrada" << endl;
         cout << "2.- Consultar entrada" << endl;
         cout << "3.- Modificar entrada" << endl;
         cout << "4.- Borrar entrada" << endl;
         cout << "5.- Documento XML" << endl;
         cout << "6.- Ver listado" << endl;
         cout << "q.- Salir" << endl;

         try {
            switch (opt = readChar ()) {
               case '1': create (storageArea); break;
               case '2': find (storageArea); break;
               case '3': update (storageArea); break;
               case '4': release (storageArea); break;
               case '5': asXML (storageArea); break;
               case '6': report (storageArea); break;
            }
         }
         catch (RuntimeException& ex) {
             ex.trace ();
             cout << ex.asString () << endl << endl;
         }
      } while (toupper (opt) != 'Q');
   }
   catch (Exception& ex) {
      cout << ex.asString () << endl;
   }

   return 0;
}

